//Originally written by Geoff McElhanon
//I modified it to fit the providers for SmartCodeGenerator.
//This modified version is written by Shahed Khan, shahed.khan@gmail.com

//==============================================================
//Copyright (C) 2004, Geoff McElhanon.  All Rights Reserved.
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//	NOTE: This copyright notice must accompany all modified versions of this code.
//
using System;
using System.IO;
using System.Xml;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;

namespace Utility.Configuration
{
	/// <summary>
	/// Summary description for CustomConfigurationSettings.
	/// </summary>
	public class CustomConfigurationSettings
	{
		private static string _configFileName = null;
		private static NameValueCollection _appSettingsCollection = null; 

		public static string ConfigFileName
		{
			get
			{
				// Initialize the config file path if not already
				if (_configFileName == null)
					_configFileName = GetCustomConfigFilePath();

				return _configFileName;
			}
			set
			{
				// Get the appropriate configuration file path/name
				_configFileName = GetCustomConfigFilePath(value);
				
				// Reset the name value collection
				_appSettingsCollection = null;
			}
		}

		public static NameValueCollection AppSettings
		{
			get
			{
				// Load settings if they haven't already
				if (_appSettingsCollection == null)
					LoadAppSettings();

				return _appSettingsCollection;
			}
		}

		private static void LoadAppSettings()
		{
			// Load the document
			XmlDocument configDoc = new XmlDocument();
			configDoc.Load(ConfigFileName);

			// Get the node settings
			XmlNode node = configDoc.SelectSingleNode("/configuration/appSettings");

			// Get reference to the NameValueCollection holding the parsed values
			NameValueSectionHandler handler = new NameValueSectionHandler();
			_appSettingsCollection = (NameValueCollection) handler.Create(null, null, node);
		}

		private static string GetCustomConfigFilePath()
		{
			// Get the full path and filename of the executing assembly (i.e. dll or exe path)
			string fullPath = Assembly.GetCallingAssembly().Location + ".config";

			// Get the default config file name
			// string configFileName = Path.GetFileName(fullPath) + ".config";

			return GetCustomConfigFilePath(fullPath);
		}

		/// <summary>
		/// Obtains the physical location of the custom configuration file with the provided file name for the calling assembly, climbing the directory hierarchy as necessary.
		/// </summary>
		/// <returns>Full path to the located configuration file.</returns>
		private static string GetCustomConfigFilePath(string customConfigFilename)
		{
			// Does the filename include path information? (i.e. if path information is provided, perform no searching)
			if (customConfigFilename.IndexOf(@"\") >= 0)
			{
				// Does it exist?
				if (!File.Exists(customConfigFilename))
					throw new FileNotFoundException(string.Format("Could not locate configuration file '{0}'.", customConfigFilename));

				// Do not perform searching, just return the configuration filename provided by the user
				return customConfigFilename;
			}
			else
			{
				// Get the full path and filename of the executing assembly (i.e. dll or exe path)
				string fullPath = Assembly.GetCallingAssembly().Location;

				// Get just the directory path
				string searchPath = Path.GetDirectoryName(fullPath);

#if !DEBUG
				// Build the expected physical config file location
				string configFileName = Path.Combine(searchPath, customConfigFilename);

				// Throw an exception if config file couldn't be found
				if (!File.Exists(configFileName))
					throw new FileNotFoundException(string.Format("Could not locate configuration file '{0}'.", customConfigFilename));

#else
				// DEBUG Implementation climbs folder structure to find config file in source folder (for easier development since VS doesn't copy DLL config files to bin folder automatically)
				string configFileName; 

				// Iterate up folder structure until we either find the file, or we don't
				do
				{
					// Build the potential physical config file location
					configFileName = Path.Combine(searchPath, customConfigFilename);
	
					// Does it exist here?
					if (File.Exists(configFileName))
						break;

					// Move to parent folder
					DirectoryInfo directory = Directory.GetParent(searchPath);
						
					if (directory == null)
					{
						searchPath = null;
					}
					else
					{
						searchPath = directory.FullName;
					}
				} while (searchPath != null);

				// Make sure that we didn't climb all the way up (and out) of the directory hierarchy
				if (searchPath == null)
				{
					throw new FileNotFoundException(string.Format("Could not locate configuration file '{0}'.", customConfigFilename));
				}
#endif

				return configFileName;
			}
		}
	}
}
